use std::sync::Arc;

use ane_system::dao::{resource::ResourceDo, ResourceVo};
use axum::{
    extract::{Path, Query},
    response::IntoResponse,
    routing::{get, put},
    Extension, Router,
};
use casbin::MgmtApi;
use common::page::{PageInfo, PageParams};
use sea_orm::TransactionTrait;
use serde::Deserialize;
use tracing::error;
use validator::Validate;

use super::SysFilter;
use crate::{
    error::{AppError, AppResult},
    extract::{Json, ValJson},
    utils::casbin_policies,
    AppState,
};

/// [Resource](ane_system::entity::sys_resource)操作相关的路由
/// - `{prefix}/` `GET|POST` [Resource](ane_system::entity::sys_resource)的查询和创建
/// - `{prefix}/:id` `PUT|DELETE` [Resource](ane_system::entity::sys_resource)的更新和删除
pub fn get_route<S>(prefix: &str) -> Router<S>
where
    S: Sync + Send + Clone + 'static,
{
    let f = move |p: &str| -> String { format!("{}{p}", prefix) };
    Router::<S>::new()
        .route(&f("/"), get(fetch_list).post(create))
        .route(&f("/:id"), put(update).delete(delete))
}

pub async fn fetch_list(
    Extension(state): Extension<AppState>,
    Query(filter): Query<SysFilter>,
) -> AppResult<Json<PageInfo<ResourceVo>>> {
    ane_system::dao::Resource::list(
        state.db(),
        filter.page.zero_page(),
        filter.page.page_size(),
        filter.path,
        filter.status,
    )
    .await
    .map(|(datas, total)| PageInfo::new(datas, &filter.page, total))
    .map(Json)
    .map_err(Into::into)
}

pub async fn create(
    Extension(state): Extension<AppState>,
    ValJson(data): ValJson<ResourceDo>,
) -> AppResult<impl IntoResponse> {
    let txn = state.db().begin().await?;
    if ane_system::dao::Resource::get_by_path::<ResourceVo>(&txn, &data.path)
        .await?
        .is_some()
    {
        Err(AppError::BadParam(format!(
            "resource path `{}` exists",
            data.path
        )))?;
    }
    let res = ane_system::dao::Resource::create(&txn, data)
        .await
        .map(Json)
        .map_err(Into::into);
    txn.commit().await?;
    res
}

pub async fn update(
    Extension(state): Extension<AppState>,
    Path(id): Path<i32>,
    ValJson(data): ValJson<ResourceDo>,
) -> AppResult<impl IntoResponse> {
    use ane_system::dao::Resource;
    let resource = Resource::get_by_id::<ResourceVo>(state.db(), id)
        .await?
        .ok_or(AppError::NotFount)?;
    let is_status_change = matches!(data.status,Some(x) if x!=resource.status);
    let is_changed = is_status_change || resource.path != data.path;
    let new_path = data.path.clone();
    let new_status = data.status;

    let txn = state.db().begin().await?;
    if matches!(Resource::get_by_path::<ResourceVo>(&txn, &data.path).await?, Some(r)if r.id!=id) {
        Err(AppError::BadParam(format!(
            "resource path `{}` exists",
            data.path
        )))?;
    }
    let res = Resource::update(&txn, id, data)
        .await
        .map(Json)
        .map_err(Into::into);
    txn.commit().await?;

    if is_changed {
        state.reload_policies().await?;
    }
    res
}

/// 删除resource信息，更新casbin
pub async fn delete(
    Extension(state): Extension<AppState>,
    Path(id): Path<i32>,
) -> AppResult<impl IntoResponse> {
    let resource = ane_system::dao::Resource::get_by_id::<ResourceVo>(state.db(), id)
        .await?
        .ok_or(AppError::NotFount)?;
    let txn = state.db().begin().await?;
    let res = ane_system::dao::Resource::delete(&txn, id)
        .await
        .map_err(Into::into)
        .map(|_| "Ok");
    txn.commit().await?;
    state.reload_policies().await?;
    res
}
